package org.gdstash.item;

import org.gdstash.file.GDFileSize;
import org.gdstash.file.GDReader;
import org.gdstash.file.GDWriter;
import org.gdstash.ui.GDStashFrame;
import org.gdstash.util.*;

import java.io.*;
import java.util.LinkedList;
import java.util.List;

public class GDStash implements GDFileSize {
    public static final int STASH_TYPE_UNENCRYPTED = 0;
    public static final int STASH_TYPE_ENCRYPTED = 1;
    public static final int STASH_VERSION_UNENCRYPTED = 9;
    public static final int STASH_VERSION_ENCRYPTED = 4;
    public static final int STASH_VERSION_ENCRYPTED_EXPANSION = 5;
    public static final int VERSION_3 = 3;
    public static final int VERSION_4 = 4;
    public static final int VERSION_5 = 5;
    public static final int VERSION_8 = 8;
    public static final String BEGIN_BLOCK = "begin_block";
    public static final String STASH_VERSION = "stashVersion";
    public static final String MOD = "mod";
    public static final String SACK_COUNT = "sackCount";
    public static final String SACK_WIDTH = "sackWidth";
    public static final String SACK_HEIGHT = "sackHeight";
    public static final String NUM_ITEMS = "numItems";
    public static final String STACK_COUNT = "stackCount";
    public static final String BASE_NAME = "baseName";
    public static final String PREFIX_NAME = "prefixName";
    public static final String SUFFIX_NAME = "suffixName";
    public static final String MODIFIER_NAME = "modifierName";
    public static final String RELIC_NAME = "relicName";
    public static final String RELIC_BONUS = "relicBonus";
    public static final String SEED = "seed";
    public static final String RELIC_SEED = "relicSeed";
    public static final String VAR1 = "var1";
    public static final String END_BLOCK = "end_block";
    public static final String X_OFFSET = "xOffset";
    public static final String Y_OFFSET = "yOffset";
    public static final String VELOCITY = "velocity";
    public static final String OWNER = "owner";
    public static final String BEGIN_BYTES;
    public static final String END_BYTES;
    private File file;
    private byte[] beginBlock;
    private int key;
    private int version;

    static {
        BEGIN_BYTES = new String(new byte[]{-50, -6, 29, -80});
        END_BYTES = new String(new byte[]{-34, -64, -83, -34});
    }
    private String modName;

    String filename;
    private byte[] baModName;
    private byte expansionStatus;
    private int numSacks;
    private byte[] endBlock;
    private List<GDStashPage> pages;
    private boolean hardcore;
    private String dirName;
    private boolean itemError;
    private boolean pageError;
    private boolean stashError;
    private boolean changed;

    public GDStash(final File file) {
        this(file, null);
    }

    public GDStash(final File file, final GDLog log) {
        this.itemError = false;
        this.pageError = false;
        this.stashError = false;
        this.changed = false;
        this.file = file;
        this.pages = new LinkedList<GDStashPage>();
        this.filename = file.getName().toUpperCase(GDConstants.LOCALE_US);
        this.hardcore = false;
        if (this.filename.endsWith(".BSH")) {
            this.hardcore = true;
        }
        if (this.filename.endsWith(".CSH")) {
            this.hardcore = true;
        }
        if (this.filename.endsWith(".GSH")) {
            this.hardcore = true;
        }
        this.read(log);
    }

    public boolean hasItemErrors() {
        return this.itemError;
    }

    public boolean hasPageErrors() {
        return this.pageError;
    }

    public boolean hasStashErrors() {
        return this.stashError;
    }

    public boolean hasChanged() {
        if (this.changed) {
            return true;
        }
        boolean change = false;
        for (final GDStashPage page : this.pages) {
            change = (change || page.hasChanged());
        }
        return change;
    }

    public int getNumberOfPages() {
        return this.numSacks;
    }

    public List<GDStashPage> getPages() {
        return this.pages;
    }

    public List<GDItem> getItems() {
        final List<GDItem> list = new LinkedList<GDItem>();
        for (final GDStashPage page : this.pages) {
            list.addAll(page.getItemList());
        }
        return list;
    }

    public boolean isHardcore() {
        return this.hardcore;
    }

    public boolean isExpansionStash() {
        return this.expansionStatus == 1;
    }
    private String displayModName;

    public int getHeight() {
        int h = 16;
        if (this.pages.isEmpty()) {
            return h;
        }
        for (final GDStashPage page : this.pages) {
            if (page == null) {
                continue;
            }
            if (h < page.height) {
                h = page.height;
            }
            for (final GDItem item : page.items) {
                if (item == null) {
                    continue;
                }
                if (item.getImage() == null) {
                    continue;
                }
                final int y = item.getY() + item.getImage().getHeight() / 32;
                if (h >= y) {
                    continue;
                }
                h = y;
            }
        }
        return h;
    }

    public int getWidth() {
        int w = 8;
        if (this.pages.isEmpty()) {
            return w;
        }
        for (final GDStashPage page : this.pages) {
            if (page != null) {
                if (w < page.width) {
                    w = page.width;
                }
                for (final GDItem item : page.items) {
                    if (item == null) {
                        continue;
                    }
                    if (item.getImage() == null) {
                        continue;
                    }
                    final int x = item.getX() + item.getImage().getWidth() / 32;
                    if (w >= x) {
                        continue;
                    }
                    w = x;
                }
            }
        }
        return w;
    }

    public File getFile() {
        return this.file;
    }

    public String getModName() {
        return this.displayModName;
    }

    public void setFile(final File file) {
        this.file = file;
        this.filename = file.getName().toUpperCase(GDConstants.LOCALE_US);
    }

    public void read(final GDLog log) {
        boolean oldFormat = false;
        String path = null;
        try (final InputStream reader = new BufferedInputStream(new FileInputStream(this.file))) {
            path = this.file.getCanonicalPath();
            String s = GDReader.readString(reader, 15);
            s = s.substring(4);
            oldFormat = s.equals("begin_block");
        } catch (FileNotFoundException ex) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_NOT_FOUND", args);
                if (log == null) {
                    GDMsgLogger.addError(msg);
                } else {
                    log.addError(msg);
                }
            }
            if (log == null) {
                GDMsgLogger.addError(ex);
            } else {
                log.addError(ex);
            }
            this.stashError = true;
        } catch (Exception ex2) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_READ", args);
                if (log == null) {
                    GDMsgLogger.addError(msg);
                } else {
                    log.addError(msg);
                }
            }
            if (log == null) {
                GDMsgLogger.addError(ex2);
            } else {
                log.addError(ex2);
            }
            this.stashError = true;
        }
        if (this.stashError) {
            return;
        }
        if (oldFormat) {
            this.readOldFormat();
        } else {
            this.readNewFormat(log);
        }
    }

    public void setModName(final String modName) {
        String s = null;
        if (modName != null && !modName.equals("")) {
            s = modName;
        }
        if (this.modName == null) {
            if (s != null) {
                this.changed = true;
                this.modName = s;
                this.displayModName = s;
                this.baModName = s.getBytes(GDConstants.CHARSET_STASH);
            }
        } else if (!this.displayModName.equals(s)) {
            this.changed = true;
            this.modName = s;
            if ((this.displayModName = s) == null) {
                this.baModName = null;
            } else {
                this.baModName = s.getBytes(GDConstants.CHARSET_STASH);
            }
        }
    }

    private void readOldFormat() {
        String path = null;
        try (final InputStream reader = new BufferedInputStream(new FileInputStream(this.file))) {
            int width = 0;
            String s = null;
            path = this.file.getCanonicalPath();
            int len = 0;
            while (len != -1) {
                len = GDReader.readUInt(reader);
                if (len != -1) {
                    s = GDReader.readString(reader, len);
                    if (s.equals("begin_block")) {
                        this.beginBlock = GDReader.readBytes4(reader);
                    }
                    if (s.equals("stashVersion")) {
                        this.version = GDReader.readUInt(reader);
                        if (this.version < 7 || this.version > 9) {
                            final Object[] args = {path, this.version};
                            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_STASH_VER", args);
                            GDMsgLogger.addError(msg);
                            this.stashError = true;
                        }
                    }
                    if (s.equals("mod")) {
                        GDReader.readBytes4(reader);
                    }
                    if (s.equals("sackCount")) {
                        this.numSacks = GDReader.readInt(reader);
                        break;
                    }
                    if (s.equals("sackWidth")) {
                        width = GDReader.readInt(reader);
                        this.numSacks = 1;
                        break;
                    }
                    continue;
                }
            }
            if (!this.stashError) {
                this.pages.clear();
                for (int i = 0; i < this.numSacks; ++i) {
                    final GDStashPage page = new GDStashPage(reader, 0, this.version, width, this.hardcore, path, i, null);
                    if (page.hasPageErrors()) {
                        this.pageError = true;
                        this.stashError = true;
                        break;
                    }
                    this.pages.add(page);
                    if (page.hasItemErrors()) {
                        this.itemError = true;
                    }
                }
                if (!this.pageError) {
                    len = GDReader.readUInt(reader);
                    if (len != -1) {
                        s = GDReader.readString(reader, len);
                        if (s.equals("end_block")) {
                            this.endBlock = GDReader.readBytes4(reader);
                        }
                    }
                }
            }
        } catch (FileNotFoundException ex) {
            if (path != null) {
                final Object[] args2 = {path};
                final String msg2 = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_NOT_FOUND", args2);
                GDMsgLogger.addError(msg2);
            }
            GDMsgLogger.addError(ex);
            this.stashError = true;
        } catch (Exception ex2) {
            if (path != null) {
                final Object[] args2 = {path};
                final String msg2 = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_READ", args2);
                GDMsgLogger.addError(msg2);
            }
            GDMsgLogger.addError(ex2);
            this.stashError = true;
        }
        this.version = -1;
    }

    private void readNewFormat(final GDLog log) {
        String path = null;
        int val = 0;
        try {
            path = this.file.getCanonicalPath();
            GDReader.readEncFileToBuffer(this.file);
            this.key = GDReader.readKey();
            val = GDReader.readEncInt(true);
            if (val != 2) {
                throw new FileVersionException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"));
            }
            final GDReader.Block block = new GDReader.Block();
            val = GDReader.readBlockStart(block);
            if (val != 18) {
                throw new FileVersionException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"));
            }
            this.version = GDReader.readEncInt(true);
            if (this.version != 3 && this.version != 4 && this.version != 5 && this.version != 8) {
                throw new FileVersionException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"));
            }
            val = GDReader.readEncInt(false);
            if (val != 0) {
                throw new FileVersionException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"));
            }
            this.baModName = GDReader.readEncStringBytes();
            this.modName = GDReader.convBytesToString(this.baModName);
            this.dirName = this.file.getParentFile().getCanonicalPath();
            if (this.dirName.length() > GDStashFrame.iniConfig.sectDir.savePath.length()) {
                this.dirName = this.dirName.substring(GDStashFrame.iniConfig.sectDir.savePath.length() + 1);
            } else {
                this.dirName = null;
            }
            if (this.modName == null) {
                this.displayModName = null;
            } else if (this.modName.length() == this.baModName.length) {
                this.displayModName = this.modName;
            } else {
                this.displayModName = this.dirName;
            }
            if (this.version >= 5) {
                this.expansionStatus = GDReader.readEncByte();
            }
            this.numSacks = GDReader.readEncInt(true);
            this.pages.clear();
            for (int i = 0; i < this.numSacks; ++i) {
                final GDStashPage page = new GDStashPage(null, 1, this.version, 8, this.hardcore, path, i, log);
                if (page.hasPageErrors()) {
                    this.pageError = true;
                    this.stashError = true;
                    break;
                }
                this.pages.add(page);
                if (page.hasItemErrors()) {
                    this.itemError = true;
                }
            }
            GDReader.readBlockEnd(block);
        } catch (FileNotFoundException ex) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_NOT_FOUND", args);
                if (log == null) {
                    GDMsgLogger.addError(msg);
                } else {
                    log.addError(msg);
                }
            }
            if (log == null) {
                GDMsgLogger.addError(ex);
            } else {
                log.addError(ex);
            }
            this.stashError = true;
        } catch (FileVersionException ex2) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_READ", args);
                if (log == null) {
                    GDMsgLogger.addError(msg);
                } else {
                    log.addError(msg);
                }
            }
            if (log == null) {
                GDMsgLogger.addInfo(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "INFO_UPDATE_GDSTASH"));
                GDMsgLogger.addError(ex2);
            } else {
                log.addInfo(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "INFO_UPDATE_GDSTASH"));
                log.addError(ex2);
            }
            this.stashError = true;
        } catch (Exception ex3) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_READ", args);
                if (log == null) {
                    GDMsgLogger.addError(msg);
                } else {
                    log.addError(msg);
                }
            }
            if (log == null) {
                GDMsgLogger.addError(ex3);
            } else {
                log.addError(ex3);
            }
            this.stashError = true;
        }
    }

    @Override
    public int getByteSize() {
        int size = 0;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        if (this.baModName != null) {
            size += this.baModName.length;
        }
        if (this.version >= 5) {
            ++size;
        }
        size += 4;
        int count = 0;
        for (final GDStashPage page : this.pages) {
            size += page.getByteSize(this.version);
            ++count;
        }
        size += 12 * count;
        size += 4;
        return size;
    }

    public void write() throws IOException {
        if (!this.hasChanged()) {
            return;
        }
        this.createBuffer();
        final String path = this.file.getParent() + GDConstants.FILE_SEPARATOR;
        String tname = "transfer.t";
        if (this.filename.endsWith(".BST")) {
            tname = "transfer.bt";
        }
        if (this.filename.endsWith(".BSH")) {
            tname = "transfer.bh";
        }
        if (this.filename.endsWith(".CST")) {
            tname = "transfer.ct";
        }
        if (this.filename.endsWith(".CSH")) {
            tname = "transfer.ch";
        }
        if (this.filename.endsWith(".GSH")) {
            tname = "transfer.h";
        }
        final File temp = new File(path + "temp_tmp.tmp");
        temp.createNewFile();
        GDWriter.writeBuffer(temp);
        File fCurr = new File(path + tname + String.format("%02d", 0));
        File fNew = null;
        for (int i = 9; i >= 0; --i) {
            fCurr = new File(path + tname + String.format("%02d", i));
            fNew = new File(path + tname + String.format("%02d", i + 1));
            if (fCurr.exists()) {
                if (i == 9) {
                    fCurr.delete();
                } else {
                    fCurr.renameTo(fNew);
                }
            }
        }
        this.file.renameTo(fCurr);
        temp.renameTo(this.file);
        this.changed = false;
    }

    public void write2() throws IOException {
        if (!this.hasChanged()) {
            return;
        }
        final String path = this.file.getParent() + GDConstants.FILE_SEPARATOR;
        String tname = "transfer.t";
        if (this.filename.endsWith(".BST")) {
            tname = "transfer.bt";
        }
        if (this.filename.endsWith(".BSH")) {
            tname = "transfer.bh";
        }
        if (this.filename.endsWith(".CST")) {
            tname = "transfer.ct";
        }
        if (this.filename.endsWith(".CSH")) {
            tname = "transfer.ch";
        }
        if (this.filename.endsWith(".GSH")) {
            tname = "transfer.h";
        }
        File fCurr = new File(path + tname + String.format("%02d", 0));
        File fNew = null;
        for (int i = 9; i >= 0; --i) {
            fCurr = new File(path + tname + String.format("%02d", i));
            fNew = new File(path + tname + String.format("%02d", i + 1));
            if (fCurr.exists()) {
                if (i == 9) {
                    fCurr.delete();
                } else {
                    fCurr.renameTo(fNew);
                }
            }
        }
        GDWriter.copyFile(this.file, fCurr);
        try {
            this.createBuffer();
            GDWriter.writeBuffer(this.file);
            this.changed = false;
        } catch (IOException ex) {
            GDWriter.copyFile(fCurr, this.file);
            throw ex;
        }
    }

    public void writeOldFormat() throws IOException {
        if (!this.hasChanged()) {
            return;
        }
        final String separator = System.getProperty("file.separator");
        String path = this.file.getCanonicalPath();
        final int pos = path.lastIndexOf(separator);
        if (pos != -1) {
            path = path.substring(0, pos + 1);
        }
        String tname = "transfer.t";
        if (this.filename.endsWith(".GSH")) {
            tname = "transfer.h";
        }
        final File temp = new File(path + "temp_tmp.tmp");
        temp.createNewFile();
        final FileOutputStream writer = new FileOutputStream(temp);
        writer.write(GDWriter.lengthToBytes4("begin_block"));
        writer.write("begin_block".getBytes(GDConstants.CHARSET_STASH));
        writer.write(this.beginBlock);
        writer.write(GDWriter.lengthToBytes4("stashVersion"));
        writer.write("stashVersion".getBytes(GDConstants.CHARSET_STASH));
        writer.write(GDWriter.intToBytes4(9));
        writer.write(GDWriter.lengthToBytes4("mod"));
        writer.write("mod".getBytes(GDConstants.CHARSET_STASH));
        writer.write(GDWriter.intToBytes4(0));
        writer.write(GDWriter.lengthToBytes4("sackCount"));
        writer.write("sackCount".getBytes(GDConstants.CHARSET_STASH));
        writer.write(GDWriter.intToBytes4(this.pages.size()));
        for (final GDStashPage page : this.pages) {
            page.writeOldFormat(writer, GDConstants.CHARSET_STASH);
        }
        writer.write(GDWriter.lengthToBytes4("end_block"));
        writer.write("end_block".getBytes(GDConstants.CHARSET_STASH));
        writer.write(this.endBlock);
        writer.flush();
        writer.close();
        File fCurr = new File(path + tname + String.format("%02d", 0));
        File fNew = null;
        for (int count = 9; count >= 0; --count) {
            fCurr = new File(path + tname + String.format("%02d", count));
            fNew = new File(path + tname + String.format("%02d", count + 1));
            if (fCurr.exists()) {
                if (count == 9) {
                    fCurr.delete();
                } else {
                    fCurr.renameTo(fNew);
                }
            }
        }
        this.file.renameTo(fCurr);
        temp.renameTo(this.file);
    }

    private void createBuffer() throws IOException {
        GDWriter.reserveBuffer(this);
        GDWriter.writeInt(1431655765);
        GDWriter.writeInt(2);
        final GDReader.Block block = new GDReader.Block();
        GDWriter.writeBlockStart(block, 18);
        if (this.version < 4) {
            this.version = 4;
        }
        GDWriter.writeInt(this.version);
        GDWriter.writeInt(0);
        GDWriter.writeStringBytes(this.baModName);
        if (this.version >= 5) {
            GDWriter.writeByte(this.expansionStatus);
        }
        GDWriter.writeInt(this.pages.size());
        for (final GDStashPage page : this.pages) {
            page.write(this.version);
        }
        GDWriter.writeBlockEnd(block);
    }
}
